﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EnumerableExtensions.cs" company="AzureContrib (Open source library http://azurecontrib.codeplex.com)">
//   Microsoft Public License (Ms-PL)
// </copyright>
// <summary>
//   Extensions on <see cref="System.Collections.IEnumerator" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using AzureContrib.WindowsAzure.Properties;

namespace AzureContrib.WindowsAzure
{
	/// <summary>
	/// Extensions on <see cref="System.Collections.IEnumerator"/>
	/// </summary>
	public static class EnumerableExtensions
	{
		/// <summary>
		/// Convert a list of Ts to a list of Us
		/// </summary>
		/// <typeparam name="TIn">The type of Ts</typeparam>
		/// <typeparam name="TOut">The type of Us</typeparam>
		/// <param name="ts">the list of Ts to convert</param>
		/// <param name="conversionFunction">The function used to convert Ts to Us</param>
		/// <returns>A list of Us</returns>
		public static IEnumerable<TOut> Convert<TIn, TOut>(this IEnumerable<TIn> ts, Func<TIn, TOut> conversionFunction)
		{
			Assumes.NotNull(conversionFunction, "conversionFunction");
			return ts.Select(conversionFunction);
		}

		/// <summary>
		/// Turns a non-generic <see cref="IEnumerable"/> into <see cref="IEnumerable{T}"/>. The assumption is that all items are of type <typeparamref name="T"/>.
		/// </summary>
		/// <typeparam name="T">The type of items expected in the collection.</typeparam>
		/// <param name="enumerable">The <see cref="IEnumerable"/> to convert.</param>
		/// <returns>The typed enumeration generated from the <paramref name="enumerable"/>.</returns>
		/// <exception cref="ArgumentOutOfRangeException">If an item found in the <paramref name="enumerable"/> is not of type <typeparamref name="T"/>.</exception>
		public static IEnumerable<T> ToTypedEnumerable<T>(this IEnumerable enumerable) where T : class
		{
			foreach (var item in enumerable)
			{
				var t = item as T;

				if (t == null)
				{
					throw new ArgumentOutOfRangeException("enumerable", string.Format(Strings.AtLeastOneItemIsOfWrongType, item.GetType().FullName, typeof(T).FullName));
				}

				yield return t;
			}
		}

		/// <summary>
		/// Any of T for non-generic <see cref="IEnumerable"/>. The assumption is that all items are of type <typeparamref name="T"/>.
		/// </summary>
		/// <typeparam name="T">The type of items expected in the collection.</typeparam>
		/// <param name="enumerable">The <see cref="IEnumerable"/> to look through.</param>
		/// <param name="predicate">The item we are searching for.</param>
		/// <returns>True if any item in the <paramref name="enumerable"/> contains an intem that gets selected by the <paramref name="predicate"/>.</returns>
		/// <exception cref="ArgumentOutOfRangeException">If an item found in the <paramref name="enumerable"/> is not of type <typeparamref name="T"/>.</exception>
		public static bool EnumerableAny<T>(this IEnumerable enumerable, Func<T, bool> predicate) where T : class
		{
			Assumes.NotNull(predicate, "predicate");
			return enumerable.ToTypedEnumerable<T>().Any(predicate);
		}

		/// <summary>
		/// Perform <paramref name="action"/> on each of the <paramref name="items"/>.
		/// </summary>
		/// <typeparam name="T">The type of object we are performing the <paramref name="action"/> on.</typeparam>
		/// <param name="items">
		/// Items to perform the action on.
		/// </param>
		/// <param name="action">
		/// Action to perform on each item.
		/// </param>
		/// <remarks>
		/// The actions are executed on the <paramref name="items"/> with deferred execution.
		/// </remarks>
		public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
		{
			if (items == null || action == null)
			{
				return;
			}

			foreach (var item in items)
			{
				action(item);
			}
		}
	}
}